#include "EncoderManager.hpp"
#include "CameraFormatUtils.hpp"
#include "cbb_log_api.h"
#include <cstring>
#include <iostream>
#include <linux/videodev2.h>

EncoderManager::EncoderManager() {}

EncoderManager::~EncoderManager() {
    for (auto &cameraEncoders : m_encoders) {
        for (auto &encoder : cameraEncoders) {
            if (encoder.handle) {
                CbbEncoderDestroy(encoder.handle);
                encoder.handle = nullptr;
            }
        }
    }
    m_encoders.clear();
}

bool EncoderManager::initializeEncoders(int cameraId, const std::vector<EncoderConfig> &configs) {
    if (cameraId >= 0 && static_cast<std::size_t>(cameraId) >= m_encoders.size()) {
        m_encoders.resize(cameraId + 1);
    }

    auto &cameraEncoders = m_encoders[cameraId];
    cameraEncoders.clear();

    for (const auto &config : configs) {
        CbbEncoderConf_t cbbConfig = convertToCbbEncoderConf(config);
        EncoderHandle handle = CbbEncoderInit(cbbConfig);

        if (handle) {
            cameraEncoders.push_back({handle, config});
            CbbLogInfo("Encoder initialized for camera %d, config: %dx%d", cameraId, config.width, config.height);
            std::cout << "Encoder initialized for camera " << cameraId << ", config: " << config.width << "x" << config.height << "format" << config.out_format << std::endl;
        } else {
            CbbLogError("Failed to initialize encoder for camera %d", cameraId);
            return false;
        }
    }

    return true;
}

bool EncoderManager::encodeFrame(int cameraId, const CbbCameraFrame_t &frame, std::vector<CbbEncoderRes_t> &results) {
    if (cameraId < 0 || static_cast<std::size_t>(cameraId) >= m_encoders.size()) {
        return false;
    }

    auto &cameraEncoders = m_encoders[cameraId];
    results.clear();
    results.resize(cameraEncoders.size());

    bool allSuccess = true;
    for (size_t i = 0; i < cameraEncoders.size(); ++i) {
        CbbCameraAckE_t ack = CbbEncoderRun(cameraEncoders[i].handle, frame, &results[i]);
        if (ack != CBB_CAMERA_ACK_OK) {
            CbbLogError("Encoding failed for camera %d, encoder %zu", cameraId, i);
            std::cout << "Encoding failed for camera " << cameraId << ", encoder " << i << std::endl;
            allSuccess = false;
        }
    }

    return allSuccess;
}

void EncoderManager::destroyEncoders(int cameraId) {
    if (cameraId >= 0 && static_cast<std::size_t>(cameraId) < m_encoders.size()) {
        for (auto &encoder : m_encoders[cameraId]) {
            if (encoder.handle) {
                CbbEncoderDestroy(encoder.handle);
                encoder.handle = nullptr;
            }
        }
        m_encoders[cameraId].clear();
    }
}
CbbEncoderConf_t EncoderManager::convertToCbbEncoderConf(const EncoderConfig &config) {
    CbbEncoderConf_t cbbConfig;

    cbbConfig.width = config.width;
    cbbConfig.height = config.height;
    cbbConfig.rotation = config.rotation;

#if 1
    // 转换 in_format
    uint32_t inFormatValue;
    if (!CameraFormatUtils::stringToFormat(config.in_format, inFormatValue)) {
        CbbLogError("Invalid input format: %s", config.in_format.c_str());
    }
    cbbConfig.in_format = inFormatValue;

    // 转换 out_format
    uint32_t outFormatValue;
    if (!CameraFormatUtils::stringToFormat(config.out_format, outFormatValue)) {
        CbbLogError("Invalid output format: %s", config.out_format.c_str());
    }
    cbbConfig.out_format = outFormatValue;

    // 处理 JPEG 特殊配置
    if (outFormatValue == V4L2_PIX_FMT_JPEG) {
        cbbConfig.special_u.jpeg_conf.qc = config.qc;
    }
#endif
    return cbbConfig;
}

bool EncoderManager::updateEncoderConfig(int cameraId, size_t encoderIndex, const EncoderConfig &newConfig) {
    if (cameraId < 0 || static_cast<std::size_t>(cameraId) >= m_encoders.size()) {
        CbbLogError("Invalid camera ID: %d", cameraId);
        return false;
    }

    auto &cameraEncoders = m_encoders[cameraId];
    if (encoderIndex >= cameraEncoders.size()) {
        CbbLogError("Invalid encoder index: %zu for camera %d", encoderIndex, cameraId);
        return false;
    }

    CbbEncoderConf_t cbbConfig = convertToCbbEncoderConf(newConfig);
    if (applyEncoderConfig(cameraEncoders[encoderIndex].handle, cbbConfig)) {
        cameraEncoders[encoderIndex].config = newConfig;
        CbbLogInfo("Encoder config updated for camera %d, encoder %zu", cameraId, encoderIndex);
        return true;
    } else {
        CbbLogError("Failed to update encoder config for camera %d, encoder %zu", cameraId, encoderIndex);
        return false;
    }
}

bool EncoderManager::applyEncoderConfig(EncoderHandle handle, const CbbEncoderConf_t &config) {
    CbbEncoderCtrlDev_t ctrlDev;
    memcpy(&ctrlDev.encoder_ctrl_u.conf_s, &config, sizeof(CbbEncoderConf_t));

    CbbCameraAckE_t result = CbbEncoderCtrl(handle, CBB_ENCODER_UPDATE_CONF, &ctrlDev);
    return (result == CBB_CAMERA_ACK_OK);
}